package org.vaadin.vaadinjit;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.vaadin.vaadinjit.data.JitChartData;

import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;


/**
 * This is the base class for AreaChart, BarChart and PieChart. These
 * visualization have many common properties.
 * 
 * @author jonkka
 *
 */
public abstract class JitChartComponent extends JitComponent<JitChartData> implements 
		JitChartData.ChangeListener {

	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** UIDL identifier for hover label events */
	public static String HOVER_LABEL_IDENTIFIER = "hoverLabel";

	/** Default margins for the visualization */
	protected static int DEFAULT_MARGIN = 5;
	
	/** Hover label event */
	private static final Method HOVER_LABEL_METHOD;
	
	/** The margins of the visualization */
	private int topMargin, bottomMargin, leftMargin, rightMargin;
	
	/** To minimize communication the hover label events are only sent from client 
	 * if needed 
	 * */
	private boolean receiveHoverLabelEvents = false;
	
	
	static {
		try {
			HOVER_LABEL_METHOD = HoverLabelListener.class.getDeclaredMethod(
					"hoverLabel", new Class[] {HoverLabelEvent.class});
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(
					"Internal error finding methods in JitChartComponent");
		}
	}
	
	/**
	 * 
	 */
	public JitChartComponent() {
		super();
				
		// Initialize margins
		topMargin = DEFAULT_MARGIN;
		bottomMargin = DEFAULT_MARGIN;
		leftMargin = DEFAULT_MARGIN;
		rightMargin = DEFAULT_MARGIN;	
	}
	
	
	@Override
	protected void initialize(PaintTarget target) throws PaintException {
		super.initialize(target);
		Map<String, Integer> margins = new HashMap<String, Integer>();
		margins.put("leftMargin", leftMargin);
		margins.put("topMargin", topMargin);
		margins.put("rightMargin", rightMargin);
		margins.put("bottomMargin", bottomMargin);
		target.addAttribute("margins", margins);
		
	}
	
	@Override
	public void changeVariables(Object source, Map<String, Object> variables) {
		super.changeVariables(source, variables);
		
		if (variables.containsKey(HOVER_LABEL_IDENTIFIER)) {
			String label = variables.get(HOVER_LABEL_IDENTIFIER).toString();
			
			// Fire a hover label event
			fireEvent(new JitChartComponent.HoverLabelEvent(this, label));
		}

	}
	
	
	/**
	 * This sets the margins of visualization. Because of technical limitations
	 * this method has to be called before the component is initialized.
	 * 
	 * @param left left margin
	 * @param top top margin
	 * @param right right margin
	 * @param bottom bottom margin
	 */
	public void setMargins(int left, int top, int right, int bottom) {
		this.topMargin = top;
		this.bottomMargin = bottom;
		this.leftMargin = left;
		this.rightMargin = right;
		
	}
	
	
	@Override
	public void setJitData(JitChartData chartData) {
		super.setJitData(chartData);
		vizData.addChangeListener(this);
		setNewDataLoaded();
	}
	
	
	@Override
	public void valueSet(String label, String dataPoint, double value) {
		setDataChanged();		
	}


	@Override
	public void dataPointAdded(String dataPoint) {
		setDataChanged();
	}


	@Override
	public void categoryAdded(String label) {
		setDataChanged();
	}
	
	@Override
	public void valuesChanged() {
		setDataChanged();
	}
	
	
	@Override
	public void paintContent(PaintTarget target) throws PaintException {
		super.paintContent(target);
		
		target.addAttribute("receiveHoverLabelEvents", receiveHoverLabelEvents);
	}
	
	
	/**
	 * Register a listener for hover label events.
	 * 
	 * @param listener
	 */
	public void addListener(HoverLabelListener listener) {
		addListener(HoverLabelEvent.class, listener, HOVER_LABEL_METHOD);
		
		// If we are not currently receiving hover label events we should start
		// doing that now
		if (!receiveHoverLabelEvents) {
			receiveHoverLabelEvents = true;
			this.requestRepaint();
		}
	}
	
	
	/**
	 * Interface for listening for a {@link HoverLabelEvent} fired by a 
	 * {@link Component}.
	 * 
	 * @author jonkka
	 *
	 */
	public interface HoverLabelListener {
		
		/** 
		 * Called when a label has been hovered in a chart.
		 * 
		 * @param event An event containing information about the event
		 */
		public void hoverLabel(HoverLabelEvent event);
		
	}
	
	
	/**
	 * Hover label event is fired when a label is hovered in a visualization
	 * 
	 * @author jonkka
	 *
	 */
	public class HoverLabelEvent extends Event {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private String label;
		
		/**
		 * New instance of hover label event.
		 * 
		 * @param source the source component of the event
		 * @param label the string of the label
		 */
		HoverLabelEvent(JitChartComponent source, String label) {
			super(source);
			this.label = label;
		}
		
		
		/**
		 * Get the string representation of the hovered label. This string 
		 * should uniquely identify the label since all label names should be 
		 * unique.
		 * 
		 * @return the label of the hovered label.
		 */
		public String getLabel() {
			return label;
		}
		
	}
	
	
}
